home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Libraries / c++advio 2.3 / Advanced i⁄o / voc_io.cc < prev    next >
Encoding:
Text File  |  1997-05-05  |  10.5 KB  |  398 lines  |  [TEXT/ttxt]

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *                Vocabulary service
  6.  *
  7.  *     I/O operations and loading/saving of vocabularies and
  8.  *                vocabulary items
  9.  *
  10.  * Simple vocabulary entries are written as follows
  11.  *    X,name= value
  12.  * where X is the one-letter item type: I for integer, S for string, D for
  13.  * double and R for vocabulary reference. value is an ascii representation
  14.  * for the item's value, in "%d" and "%g" printf-format for integer and
  15.  * double items correspondingly, and "" or a file_name for a VocRef item.
  16.  * String values are quoted (in double quotes) strings. A double-quote
  17.  * sign within the string body is escaped with the backslash, and 
  18.  * backslash itself within the string body is doubled.
  19.  *
  20.  * Vocabulary is written as follows
  21.  *    Voc(X...X): name= "comment" (
  22.  *    vocabulary entry1
  23.  *    ......
  24.  *    vocabulary entryn
  25.  *    )
  26.  *
  27.  * where X is an option code (R for read-only, H for homegeneous)
  28.  * comment is written as a quoted string
  29.  *
  30.  * $Id: voc_io.cc,v 1.3 1997/03/04 21:02:34 oleg Exp oleg $
  31.  *
  32.  ************************************************************************
  33.  */
  34.  
  35. #ifdef __GNUC__
  36. #pragma implementation
  37. #endif
  38.  
  39.  
  40. #include "myenv.h"
  41. #include <ctype.h>
  42. #include <std.h>
  43. #include <fstream.h>
  44.  
  45. #include "voc.h"
  46.  
  47. //------------------------------------------------------------------------
  48. //            Service I/O functions
  49.  
  50.                 // Write a string with the appropriate quoting
  51. static void write_quoted_string(ostream& outs, const char * str)
  52. {
  53.   assert( outs.good() );
  54.   outs << '"';                // Start with a quote
  55.   for(register const char * p=str; *p; p++)
  56.     if( *p == '"' )
  57.      outs << "\\\"";
  58.     else if( *p == '\\' )
  59.      outs << "\\\\";
  60.     else
  61.       outs << *p;
  62.   outs << '"';                // Finish with a quote
  63.   assert( outs.good() );
  64. }
  65.  
  66.                 // Read a character from the input stream
  67.                 // and compare it with one of the expected
  68.                 // characters (specified as a string)
  69.                 // If the read character was expected, return
  70.                 // its index in the expected char string
  71.                 // Otherwise, write a nasty message
  72. static int expect_char(istream& ins, const char * expected_chars,
  73.                const char * comment)
  74. {
  75.   assert( ins.good() );
  76.   char c;
  77.   assert( ins.get(c).good() );
  78.   register char * p = strchr(expected_chars,c);
  79.   if( p == 0 )
  80.     _error("Wrong character %c (0x%x) %s, '%s' expected",
  81.        c,c,comment,expected_chars);
  82.   return p-expected_chars;
  83. }
  84.                 // Read a quoted string
  85.                 // Returns a ptr to a globally allocated
  86.                 // string. We can read strings as big as
  87.                 // they can be (as they have been written)
  88. static char * read_quoted_string(istream& ins)
  89. {
  90.   assert( ins.good() );
  91.   char c;
  92.  
  93.   expect_char(ins,"\"","starting a quoted string");
  94.  
  95.   const int quantum = 100;
  96.   int allocated_size = 100;
  97.   char * str = (char *)malloc(allocated_size);    // Do initial allocation
  98.   assert( str != 0 );
  99.   register int i = 0;                // Current position
  100.   
  101.   for(;;)
  102.   {
  103.     assert( ins.get(c).good() );
  104.     if( c == '\"' )
  105.       break;                    // Final quote is encountered
  106.     if( c == '\\' )
  107.     {
  108.       ins >> c;                    // Read the escaped character
  109.       assert( ins.good() );
  110.     }
  111.     str[i++] = c;
  112.     if( i == allocated_size - 1 )
  113.       assert( (str = (char *)realloc(str,allocated_size+=quantum)) != 0 );
  114.   }
  115.  
  116.   assert( ins.good() );
  117.   assert( i < allocated_size - 1 );
  118.  
  119.   str[i++] = '\0';            // Terminate the string
  120.   if( allocated_size - i > 10 )        // Get rid of big slack if any
  121.       assert( (str = (char *)realloc(str,i < 8 ? 8 : i)) != 0 );
  122.  
  123.   return str;
  124. }
  125.  
  126. //------------------------------------------------------------------------
  127. //            Writing Simple Vocabulary Items
  128.  
  129.                 // Write the type and the name
  130.                 // (and leave dumping of the value to
  131.                 // derived classes)
  132. void GenericVocItem::write_down(ostream& outs) const
  133. {
  134.   assert( val_type != Vocab );        // Voc needs a special treatment
  135.   outs << ( val_type == Int ? 'I' : val_type == Double ? 'D' :
  136.        val_type == String ? 'S' : val_type == VocRef ? 'R' : 'X' );
  137.   outs << ',' << name << "= ";
  138. }
  139.  
  140.  
  141. void IntVocItem::write_down(ostream& outs) const
  142. {
  143.   GenericVocItem::write_down(outs);
  144.   outs << value << endl;
  145. }
  146.  
  147. void DoubleVocItem::write_down(ostream& outs) const
  148. {
  149.   GenericVocItem::write_down(outs);
  150.   outs << value << endl;
  151. }
  152.  
  153. void StrVocItem::write_down(ostream& outs) const
  154. {
  155.   GenericVocItem::write_down(outs);
  156.   write_quoted_string(outs,value);
  157.   outs << endl;
  158. }
  159.  
  160. void VocRefItem::write_down(ostream& outs) const
  161. {
  162.   GenericVocItem::write_down(outs);
  163.   write_quoted_string(outs,voc_file_name ? voc_file_name : "");
  164.   outs << endl;
  165. }
  166.  
  167. //------------------------------------------------------------------------
  168. //            Reading Simple Vocabulary Items
  169.  
  170.             // Read in and parse val_type & name
  171.             // and return an item of an appropriate type
  172.             // That is, if IntVocItem is being read, an IntVocItem
  173.             // is returned
  174.             // The function is declared as static member
  175.             // function: on the one hand, the method cannot be
  176.             // applied to an object (because a (derived) object
  177.             // is being constructed by this function). The function
  178.             // could've been declared as friend, but it must be
  179.             // protected to prevent anybody from constructing
  180.             // dangling items (not in any dictionary)
  181. GenericVocItem * GenericVocItem::read_in(istream& ins)
  182. {
  183.   assert( ins.good() );
  184.   int c = ins.peek();            // Get a type character and analyze it
  185.   Type type = (Type)(-1);        // Initialize with some insane value
  186.  
  187.   switch(c)
  188.   {
  189.     case 'I':
  190.          type = Int;
  191.      break;
  192.  
  193.     case 'D':
  194.          type = Double;
  195.      break;
  196.      
  197.     case 'S':
  198.      type = String;
  199.      break;
  200.  
  201.     case 'V':
  202.      return new Voc(ins);
  203.  
  204.     case 'R':
  205.      type = VocRef;
  206.      break;
  207.  
  208.     default:
  209.      _error("Reading VocItem: unexpected type letter %c (0x%x)",c,c);
  210.   }
  211.  
  212.   ins.ignore(1);
  213.   expect_char(ins,",","after the type letter");
  214.  
  215.   char read_name[34];
  216.   ins.get(read_name,sizeof(read_name),'=');        // Read the name
  217.   assert( ins.good() );
  218.  
  219.   expect_char(ins,"=","after the name");
  220.   expect_char(ins," ","after the name");
  221.  
  222.   switch(type)
  223.   {
  224.     case Int:
  225.          return new IntVocItem(read_name,ins);
  226.  
  227.     case Double:
  228.          return new DoubleVocItem(read_name,ins);
  229.  
  230.     case String:
  231.          return new StrVocItem(read_name,ins);
  232.  
  233.     case VocRef:
  234.          {
  235.        const char * voc_file_name = read_quoted_string(ins);
  236.        if( voc_file_name[0] == '\0' )
  237.          free((char *)voc_file_name), voc_file_name = 0;
  238.            return new VocRefItem(read_name,voc_file_name);
  239.      }
  240.  
  241.     default:
  242.      _error("can't happen");
  243.   }
  244.   return 0;                // as I said, it can't happen
  245. }
  246.  
  247.                 // Read the value of the IntVocItem
  248. IntVocItem::IntVocItem(const char * _name, istream& ins)
  249.     : GenericVocItem(_name,GenericVocItem::Int)
  250. {
  251.   ins >> value;
  252.   assert( ins.good() );
  253. }
  254.  
  255.                 // Read the value of the Double Item
  256. DoubleVocItem::DoubleVocItem(const char * _name, istream& ins)
  257.     : GenericVocItem(_name,GenericVocItem::Double)
  258. {
  259.   ins >> value;
  260.   assert( ins.good() );
  261. }
  262.  
  263.                 // Read the value of the StrVocItem
  264. StrVocItem::StrVocItem(const char * _name, istream& ins)
  265.     : GenericVocItem(_name,GenericVocItem::String),
  266.       value(read_quoted_string(ins))
  267. {
  268. }
  269.  
  270. //------------------------------------------------------------------------
  271. //            Reading/Writing a vocabulary
  272.  
  273. void Voc::write_down(ostream& outs) const
  274. {
  275.   outs << "Voc(";
  276.  
  277.   if( check_option(ReadOnly) )
  278.     outs << 'R';
  279.  
  280.   if( check_option(Homogeneous) )
  281.     outs << 'H';
  282.  
  283.   outs << "): " << name << "= ";
  284.   write_quoted_string(outs,comment);
  285.   outs << " (\n";
  286.  
  287.   for( GenericVocItem * ip=first; ip != 0; ip=ip->next )
  288.     (*ip).write_down(outs);
  289.  
  290.   outs << ")\n";
  291.   assert( outs.good() );
  292.   (*(Voc *)this).reset_option(Dirty);        // Cast away const at 'this'
  293. }
  294.  
  295.                 // Load a vocabulary from the istream
  296. Voc::Voc(istream& ins)
  297.     : GenericVocItem("",GenericVocItem::Vocab),
  298.       first(0), last(0), comment(0)
  299. {
  300.   (int&)options = 0;
  301.  
  302.   {
  303.     assert( ins.good() );
  304.     char voc_string[4+1];        // Check for the "Voc(" string
  305.     assert( ins.get(voc_string,sizeof(voc_string)).gcount() ==
  306.         sizeof(voc_string)-1 );
  307.     if( strcmp(voc_string,"Voc(") != 0 )
  308.       _error("Wrong signature of the vocabulary '%s', 'Voc(' was expected",
  309.          voc_string);
  310.   }
  311.  
  312.   register int i;
  313.   int should_be_read_only = 0;
  314.   while( (i=expect_char(ins,"RH)","voc option char")) != 2 )
  315.     if( i == 0 )
  316.       should_be_read_only = 1;
  317.     else if( i == 1 )
  318.       set_option(Homogeneous);
  319.     else
  320.       _error("Can't happen");
  321.  
  322.   expect_char(ins,":","after reading voc options");
  323.   expect_char(ins," ","after reading voc options");
  324.  
  325.   ins.get(name,sizeof(name),'=');
  326.   expect_char(ins,"=","after reading voc name");
  327.   expect_char(ins," ","after reading voc name");
  328.  
  329.   comment = read_quoted_string(ins);
  330.  
  331.   expect_char(ins," ","after reading voc comment");
  332.   expect_char(ins,"(","after reading voc comment");
  333.  
  334.   for(;;)            // Reading Voc items
  335.   {
  336.     assert( ins >> ws );        // it skips white spaces (incl \n)
  337.     int c = ins.peek();
  338.     if( c == ')' )
  339.       break;                // End-of-voc reached
  340.     *this += *read_in(ins);        // read an item and put in into the
  341.                     // vocabulary
  342.   }
  343.   expect_char(ins,")","after reading voc items");
  344.   ins >> ws;                // it skips white spaces (incl \n)
  345.  
  346.   if( should_be_read_only )        // After we've loaded all elements,
  347.      set_option(ReadOnly);        // set the read-only flag if necessary
  348. }
  349.  
  350.                 // Load from the file (using VOCPATH if
  351.                 // necessary) and VocOfVoc_catalog
  352.                 // VOCPATH has the same form as PATH
  353.                 // and acts the same way
  354. Voc& Voc::load(const char * file_name)
  355. {
  356.   const char * path_env_name = "VOCPATH";
  357.  
  358.   if( file_name == 0 )
  359.     _error("Voc cannot be loaded: file name is empty");
  360.  
  361.   ifstream ins;
  362.   if( file_name[0] == '/' )        // Absolute name was specified
  363.   {
  364.     ins.open(file_name);
  365.     if( !ins.good() )
  366.       perror("opening a file"),
  367.       _error("Failed to open file '%s' to load a voc",file_name);
  368.   }
  369.   else                    // Try every path of VOCPATH in turn
  370.   {                    // Assume "." (curr dir path) if
  371.     char * path = getenv(path_env_name);    // VOCPATH isn't set
  372.     path = ( path != 0 && path[0] != '\0' ? strdup(path) : strdup(".") );
  373.     assert( path != 0 );
  374.     for(const char * curr_path = strtok(path,":"); curr_path != 0;
  375.     curr_path = strtok(0,":"))
  376.     {
  377.       if( curr_path[0] == '\0' )
  378.     continue;
  379.  
  380.       char full_file_name[300];
  381.       sprintf(full_file_name, curr_path[strlen(curr_path)-1] == '/' ? "%s%s" :
  382.           "%s/%s",curr_path,file_name);
  383.       ins.open(full_file_name);
  384.       if( ins.good() )
  385.     break;
  386.     }
  387.     if( !ins.good() )
  388.       _error("Failed to open file '%s' to load using VOCPATH '%s'",
  389.          file_name,path);
  390.     free(path);
  391.   }
  392.  
  393.   assert( ins.good() );
  394.   Voc * vp = new Voc(ins);
  395.   (*vp).VocOfVoc_catalog();
  396.   return *vp;
  397. }
  398.